/************************************************************
   Copyright 2008 Abhinav Pobbati

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*************************************************************/

#pragma once

#pragma warning(disable: 4793)
#pragma warning(disable: 4172)
#pragma warning(disable: 4461)
#pragma warning(disable: 4947)
#pragma warning(disable: 4003)

#define ALLOC_StringToCStr_ANSI(ptrName,var) IntPtr _##ptrName## = Marshal::StringToHGlobalAnsi(var);\
	const char* ptrName = static_cast<const char*>(_##ptrName##.ToPointer());

#define HK_CLASS_INTERNALS(className,hkType) \
className##(##hkType## *instance, bool free) { _setThis(instance, free); }\
##className::!##className##() { if(__this != nullptr) { if(_free && hkBaseSystem::isInitialized()) {delete __this;}\
__this = nullptr; _free = false; } }\
void className##::_setThis(##hkType## *instance, bool free) { __this = instance; _free = free; }\
virtual hkType## &##className##::_ref() { return (*__this); }\
virtual hkType## *##className##::_this() { return (hkType*) __this; }\
##hkType## *__this;bool _free;\
className##(Havok4Xna::ObjectCreation creationType) { _setThis(nullptr, false); }\
public:virtual void *className##::GetNativePointer() { return __this; }\
virtual void className##::SetNativePointer(void *ptr, bool free) { __this = (hkType*) ptr; _free = free; }

#define HK_DERIVED_CLASS_INTERNALS(className,hkType,baseClass) \
className##(##hkType## *instance, bool free): baseClass(instance, free) {}\
virtual hkType## &##className##::_ref() new { return (*(hkType*) __this); }\
virtual hkType## *##className##::_this() new { return (hkType*) __this; }\
className##(Havok4Xna::ObjectCreation creationType) : baseClass(creationType) {}

#define HK_CLASS_INTERNALS_1(className,hkType) \
className##(##hkType## *instance, bool free) { _setThis(instance, free); }\
##className::!##className##() { if(__this != nullptr) { __this = nullptr; _free = false; }}\
void className##::_setThis(##hkType## *instance, bool free) { __this = instance; _free = free;}\
virtual hkType## &##className##::_ref() { return (*__this); }\
virtual hkType## *##className##::_this() { return (hkType*) __this; }\
className##(Havok4Xna::ObjectCreation creationType) { _setThis(nullptr, false); }\
##hkType## *__this;bool _free;\
public:virtual void *className##::GetNativePointer() { return __this; }\
virtual void className##::SetNativePointer(void *ptr, bool free) { __this = (hkType*) ptr; _free = free; }

#define HK_STRUCT_INTERNALS(className,hkType) \
className##(##hkType## *instance, bool free) { _setThis(instance, free); }\
##className::!##className##() { if(__this != nullptr) { if(_free && hkBaseSystem::isInitialized()) {delete __this;}\
__this = nullptr; _free = false; } }\
void className##::_setThis(##hkType## *instance, bool free) { __this = instance; _free = free;}\
hkType## &##className##::_ref() { return (*__this); }\
virtual hkType## *##className##::_this() { return (hkType*) __this; }\
private:##hkType## *__this;bool _free;

//// Simple properties ////
#define DECLARE_PROPERTY_GET(propType,propName) \
	property propType propName { propType get(); }

#define DECLARE_PROPERTY_SET(propType,propName) \
	property propType propName { void set(propType value); }

#define DECLARE_PROPERTY_GETSET(propType,propName) \
	property propType propName { propType get(); void set(propType value); }


#define IMPL_SIMPLE_PROPERTY_GET(propType,propName,nativePropName,managedClass)\
	propType managedClass##::##propName##::get() { return _ref().##nativePropName##; }

#define IMPL_SIMPLE_PROPERTY_SET(propType,propName,nativePropName,managedClass)\
	void managedClass##::##propName##::set(propType value) { _ref().##nativePropName = value; }

#define IMPL_SIMPLE_PROPERTY_GETSET(propType,propName,nativePropName,managedClass)\
	propType managedClass##::##propName##::get() { return _ref().##nativePropName##; }\
	void managedClass##::##propName##::set(propType value) { _ref().##nativePropName = value; }

#define IMPL_SIMPLE_PROPERTY_GETTER(propType,propName,nativePropGetter,managedClass)\
	propType managedClass##::##propName##::get() { return _ref().nativePropGetter(); }

#define IMPL_SIMPLE_PROPERTY_SETTER(propType,propName,nativePropSetter,managedClass)\
	void managedClass##::##propName##::set(propType value) { _ref().nativePropSetter(value); }

#define IMPL_SIMPLE_PROPERTY_GETTERSETTER(propType,propName,nativePropGetter,nativePropSetter,managedClass)\
	propType managedClass##::##propName##::get() { return _ref().nativePropGetter(); }\
	void managedClass##::##propName##::set(propType value) { _ref().nativePropSetter(value); }

#define IMPL_GENERIC_SIMPLE_PROPERTY_GETTERSETTER(propType,propName,getter,setter,managedClass)\
	propType managedClass##::##propName##::get() { return getter; }\
	void managedClass##::##propName##::set(propType value) { setter = value; }

////////////////////////////


//// Complex properties ////
#define DECLARE_COMPLEX_PROPERTY_GET(propType,propName) \
	public:property propType ^propName { propType ^get(); }\
	private:propType ^_##propName;public:

#define DECLARE_COMPLEX_PROPERTY_SET(propType,propName) \
	property propType ^propName { void set(propType ^value); }

#define DECLARE_COMPLEX_PROPERTY_GETSET(propType,propName) \
	public:property propType ^propName { propType ^get(); void set(propType ^value); }\
	private:propType ^_##propName;public:

#define IMPL_COMPLEX_PROPERTY_GET(className,propName,propType,nativeClassType,nativePropName,nativePropType)\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType((nativePropType*) &_ref().nativePropName, false); }\
return _##propName;}

#define IMPL_COMPLEX_PROPERTY_SET(className,propName,propType,nativeClassType,nativePropName,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType &r) { s.nativePropName = r; }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_ref());}

#define IMPL_COMPLEX_PROPERTY_GETSET(className,propName,propType,nativeClassType,nativePropName,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType &r) { s.nativePropName = r; }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_ref());}\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType((nativePropType*) &_ref().nativePropName, false); }\
return _##propName;}

#define IMPL_COMPLEX_PROPERTY_GET_2(className,propName,propType,nativeClassType,nativePropName,nativePropType)\
propType ^className::propName::get() {\
if(_ref().nativePropName == nullptr) return nullptr;if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }\
_##propName->_setThis(_ref().nativePropName, false); return _##propName;}

#define IMPL_COMPLEX_PROPERTY_SET_2(className,propName,propType,nativeClassType,nativePropName,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType *r) { s.nativePropName = r; }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_this());}

#define IMPL_COMPLEX_PROPERTY_GETSET_2(className,propName,propType,nativeClassType,nativePropName,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType *r) { s.nativePropName = r; }\
void className::propName::set(propType ^v){\
if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }\
className##_##propName##_set(_ref(), v->_this());_##propName->_setThis(_ref().nativePropName, false);}\
propType ^className::propName::get() {\
if(_ref().nativePropName == nullptr) return nullptr;if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }\
_##propName->_setThis(_ref().nativePropName, false); return _##propName;}

#define IMPL_COMPLEX_PROPERTY_GETTER(className,propName,propType,nativeClassType,nativeGetter,nativePropType)\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType((nativePropType*) &_ref().nativeGetter(), false); }\
return _##propName;}

#define IMPL_COMPLEX_PROPERTY_SETTER(className,propName,propType,nativeClassType,nativeSetter,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType &r) { s.nativeSetter(r); }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_ref());}

#define IMPL_COMPLEX_PROPERTY_GETTERSETTER(className,propName,propType,nativeClassType,nativePropGetter,nativePropSetter,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType &r) { s.nativePropSetter(r); }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_ref());}\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType((nativePropType*) &_ref().nativePropGetter(), false); }\
return _##propName;}

#define IMPL_COMPLEX_PROPERTY_GETTER_2(className,propName,propType,nativeClassType,nativeGetter,nativePropType)\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType((nativePropType*) _this()->nativeGetter(), false); }\
return _##propName;}

#define IMPL_COMPLEX_PROPERTY_SETTER_2(className,propName,propType,nativeClassType,nativeSetter,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType *r) { s.nativeSetter(r); }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_this());}

#define IMPL_COMPLEX_PROPERTY_GETTERSETTER_2(className,propName,propType,nativeClassType,nativePropGetter,nativePropSetter,nativePropType)\
inline static void className##_##propName##_set(nativeClassType &s, nativePropType *r) { s.nativePropSetter(r); }\
void className::propName::set(propType ^v){className##_##propName##_set(_ref(), v->_this());}\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType((nativePropType*) _this()->nativePropGetter(), false); }\
return _##propName;}

#define IMPL_GENERIC_COMPLEX_PROPERTY_GET(className, propName, propType, val)\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }\
_##propName->_setThis(val, false); return _##propName;}

#define IMPL_GENERIC_COMPLEX_PROPERTY_SET(className, propName, propType, lval, rval, getter)\
void className::propName::set(propType ^v) {\
if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }lval = rval;}

#define IMPL_GENERIC_COMPLEX_PROPERTY_GETSET(className, propName, propType, val, lval, rval)\
propType ^className::propName::get() {\
if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }\
_##propName->_setThis(val, false); return _##propName;}\
void className::propName::set(propType ^v) {\
if(_##propName == nullptr) {_##propName = gcnew propType(nullptr, false); }lval = v->rval;}

//////////////////////////////

#define ATTR_DEPRECATED [System::Obsolete("This class is deprecated.", false)]
